use crate::db::define::SYSTEM_ID;
use crate::db::entities::tag::{Model, TYPE_USER_DEFINED};
use crate::db::entities::tag_relation;
use crate::db::{db_tag, db_tag_relation};
use crate::rest_api::api_tag::{CreateParams, SearchParams, Tag, UpdateParams};
use crate::rest_api::api_user::UserToken;
use anyhow::Result;
use rbase::define::{ApiError, BaseError};
use rbase::id_utils::str2id;

pub async fn fing_tag_by_id_with_user(user_token: &UserToken, tag_id_str: &String) -> Result<Model, ApiError> {
    let tag_id = str2id(&tag_id_str)?;
    let tag_model = db_tag::find_by_id_and_owner_ids(tag_id, &user_token.owner_ids)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(tag_id_str.to_string()))?;
    return Ok(tag_model);
}

pub async fn find_tag_by_id(user_token: &UserToken, tag_id: &String) -> Result<Tag> {
    let tag_model = fing_tag_by_id_with_user(user_token, tag_id).await?;
    Ok(Tag::from_model(&tag_model))
}

pub async fn delete_tag_by_id(user_token: &UserToken, tag_id: &String) -> Result<(), ApiError> {
    let tag_model = fing_tag_by_id_with_user(user_token, tag_id).await?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && tag_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_tag::delete_by_id(tag_model.id).await?;
    db_tag_relation::delete_by_tag_id(tag_model.id).await?;
    Ok(())
}

pub async fn update_tag_by_id(user_token: &UserToken, tag_id: &String, new_tag: &UpdateParams) -> Result<(), ApiError> {
    let old_model = fing_tag_by_id_with_user(user_token, tag_id).await?;
    // 普通用户不能修改系统资源
    if !user_token.is_admin && old_model.owner_id == Some(SYSTEM_ID) {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: new_tag.name.clone(),
        type_id: new_tag.type_id,
        ..Default::default()
    };
    db_tag::update(new_model).await?;
    Ok(())
}

pub async fn create_tag(user_token: &UserToken, new_tag: &CreateParams) -> Result<Tag> {
    let new_model = Model {
        owner_id: Some(user_token.id),
        name: Some(new_tag.name.clone()),
        type_id: Some(TYPE_USER_DEFINED),
        ..Default::default()
    };
    let created_model = db_tag::insert(new_model).await?;
    Ok(Tag::from_model(&created_model))
}

pub async fn search(user_token: &UserToken, tag_search_params: &SearchParams) -> Result<(Vec<Tag>, i64)> {
    let (tags, total) = db_tag::search(&db_tag::SearchParams {
        name: tag_search_params.name.clone(),
        type_id: tag_search_params.type_id,
        offset: tag_search_params.offset,
        limit: tag_search_params.limit,
        order_by: tag_search_params.order_by.clone(),
        order: tag_search_params.order.clone(),
        owner_ids: user_token.owner_ids.clone(),
    })
    .await?;
    Ok((tags.into_iter().map(|tag| Tag::from_model(&tag)).collect(), total))
}

pub async fn validate_tag_id(user_token: &UserToken, tag_id: Option<String>) -> Result<Option<i64>> {
    match tag_id {
        Some(tag_id) => {
            let tag = fing_tag_by_id_with_user(user_token, &tag_id).await?;
            Ok(Some(tag.id))
        }
        None => Ok(None),
    }
}

pub async fn validate_tag_ids(user_token: &UserToken, tag_ids: &Option<Vec<String>>) -> Result<Option<Vec<i64>>, ApiError> {
    match tag_ids {
        Some(tag_ids) => {
            let mut validated_ids = Vec::new();
            for tag_id_str in tag_ids {
                let tag_id = str2id(&tag_id_str)?;
                db_tag::find_by_id_and_owner_ids(tag_id, &user_token.owner_ids)
                    .await?
                    .ok_or_else(|| BaseError::ResourceNotFound(tag_id_str.to_string()))?;
                validated_ids.push(tag_id);
            }
            Ok(Some(validated_ids))
        }
        None => Ok(None),
    }
}

pub async fn update_other_id_with_tag_ids(user_token: &UserToken, other_id: i64, tag_ids: &Vec<i64>) -> Result<()> {
    let mut ids = tag_ids.clone();

    let old_tag_relation = if user_token.is_admin {
        db_tag_relation::find_by_other_id(other_id).await?
    } else {
        db_tag_relation::find_by_other_id_and_owner_ids(other_id, &Some(vec![user_token.id])).await?
    };
    for relation in old_tag_relation {
        match ids.iter().position(|&i| i == relation.tag_id.unwrap_or(0)) {
            Some(index) => {
                ids.remove(index);
            }
            None => {
                db_tag_relation::delete_by_id(relation.id).await?;
            }
        }
    }
    for id in ids {
        db_tag_relation::insert(tag_relation::Model {
            owner_id: Some(user_token.id),
            tag_id: Some(id),
            other_id: Some(other_id),
            ..Default::default()
        })
        .await?;
    }

    Ok(())
}
